package edu.nf.relaxenjoy.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import edu.nf.relaxenjoy.common.constant.Constant;
import edu.nf.relaxenjoy.common.constant.platform.PlatformConstant;
import edu.nf.relaxenjoy.common.constant.platform.RabbitMqConstant;
import edu.nf.relaxenjoy.common.exception.CommonException;
import edu.nf.relaxenjoy.common.result.IPage;
import edu.nf.relaxenjoy.common.util.ConvertUtils;
import edu.nf.relaxenjoy.common.util.JsonUtils;
import edu.nf.relaxenjoy.common.util.PageUtils;
import edu.nf.relaxenjoy.common.util.RedisUtils;
import edu.nf.relaxenjoy.domain.dto.CommentDTO;
import edu.nf.relaxenjoy.domain.entity.Comment;
import edu.nf.relaxenjoy.domain.entity.Post;
import edu.nf.relaxenjoy.domain.entity.User;
import edu.nf.relaxenjoy.domain.vo.CommentVo;
import edu.nf.relaxenjoy.domain.vo.UserRecordVo;
import edu.nf.relaxenjoy.mapper.CommentMapper;
import edu.nf.relaxenjoy.mapper.PostMapper;
import edu.nf.relaxenjoy.mapper.UserMapper;
import edu.nf.relaxenjoy.service.CommentService;
import edu.nf.relaxenjoy.service.PlatformDataToCache;
import edu.nf.relaxenjoy.service.PlatformProducer;
import edu.nf.relaxenjoy.web.websocket.WebsocketServer;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhu
 * @date 2024-12-16
 */
@Service
@RequiredArgsConstructor
public class CommentServiceImpl implements CommentService {

    private final CommentMapper commentMapper;

    private final UserMapper userMapper;

    private final PostMapper postMapper;

    private final RedisUtils redisUtils;

    private final PlatformProducer platformProducer;

    private final PlatformDataToCache platformDataToCache;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommentVo addComment(CommentDTO commentDTO) {
        Comment comment = ConvertUtils.sourceToTarget(commentDTO, Comment.class);

        commentMapper.insert(comment);

        Post post = postMapper.getById(commentDTO.getMid());

        if (post.getCommentCount() <= 100) {
            post.setCommentCount(post.getCommentCount() + 1);
            platformProducer.sendMessage(RabbitMqConstant.POST_STATE_EXCHANGE, RabbitMqConstant.POST_STATE_KEY, post);
        } else {
            String postStateKey = PlatformConstant.POST_STATE + commentDTO.getMid();
            platformDataToCache.postDataToCache(post, postStateKey, 2, 1);
        }

        String userRecordKey = PlatformConstant.USER_RECORD + post.getUserId();
        UserRecordVo userRecordVo;

        if (Boolean.TRUE.equals(redisUtils.hasKey(userRecordKey)) && !commentDTO.getUid().equals(post.getUserId())) {
            userRecordVo = JsonUtils.parseObject(redisUtils.get(userRecordKey), UserRecordVo.class);
            userRecordVo.setNoreplyCount(userRecordVo.getNoreplyCount() + 1);
            redisUtils.set(userRecordKey, JSON.toJSONString(userRecordVo));
            //如果当前点赞的用户是本用户不需要通知
            try {
                WebsocketServer.sendMessageTo(JSON.toJSONString(userRecordVo), String.valueOf(post.getUserId()));
            } catch (Exception e) {
                throw new CommonException(Constant.MSG_ERROR);
            }
        }

        //如果是二级评论
        if (commentDTO.getReplyId() != 0) {
            Comment rootComment = commentMapper.getById(commentDTO.getPid());
            rootComment.setTwoNums(rootComment.getTwoNums() + 1);
            platformProducer.sendMessage(RabbitMqConstant.COMMENT_STATE_EXCHANGE, RabbitMqConstant.COMMENT_STATE_KEY, rootComment);

            //通知回复的用户(不是当前用户才通知)
            Comment replyComment = commentMapper.getById(commentDTO.getReplyId()); //原评论
            String replyUserRecordKey = PlatformConstant.USER_RECORD + replyComment.getUid();

            if (Boolean.TRUE.equals(redisUtils.hasKey(replyUserRecordKey)) && !replyComment.getUid().equals(commentDTO.getUid())) {
                UserRecordVo replyUserRecordVo = JsonUtils.parseObject(redisUtils.get(replyUserRecordKey), UserRecordVo.class);
                replyUserRecordVo.setNoreplyCount(replyUserRecordVo.getNoreplyCount() + 1);
                redisUtils.set(replyUserRecordKey, JSON.toJSONString(replyUserRecordVo));

                try {
                    WebsocketServer.sendMessageTo(JSON.toJSONString(replyUserRecordVo), String.valueOf(replyComment.getUid()));
                } catch (Exception e) {
                    throw new CommonException(Constant.MSG_ERROR);
                }
            }
        }
        CommentVo commentVo = ConvertUtils.sourceToTarget(comment, CommentVo.class);
        User user = userMapper.getUserByUserId(comment.getUid());

        commentVo.setAvatar(user.getAvatar());
        commentVo.setUsername(user.getUsername());
        commentVo.setCreateDate(comment.getCreateTime());

        if (comment.getReplyUid() != 0) {
            User replyUser = userMapper.getUserByUserId(comment.getReplyUid());
            commentVo.setReplyName(replyUser.getUsername());
        }

        return commentVo;
    }

    @Override
    public IPage<CommentVo> getAllComment(long page, long limit, Long mid, Long uid) {
        // 开启分页
        PageHelper.startPage((int) page, (int) limit);

        // 获取所有的一级评论
        List<Comment> commentOneList = commentMapper.getCommentsByMidAndPid(mid, 0L);

        // 获取评论的分页信息
        PageInfo<Comment> pageInfo = new PageInfo<>(commentOneList);

        // 提取用户 ID 列表
        List<Long> uidList = commentOneList.stream().map(Comment::getUid).collect(Collectors.toList());

        // 查询用户信息
        List<User> userList = new ArrayList<>();
        Map<Long, User> userMap = new HashMap<>();
        if (!uidList.isEmpty()) {
            userList = userMapper.listByIds(uidList);
            userMap = userList.stream().collect(Collectors.toMap(User::getUserId, user -> user));
        }
        List<CommentVo> commentVoList = new ArrayList<>();

        for (Comment model : commentOneList) {
            CommentVo commentVo = ConvertUtils.sourceToTarget(model, CommentVo.class);
            User user = userMap.get(model.getUid());

            commentVo.setUsername(user.getUsername());
            commentVo.setAvatar(user.getAvatar());
            commentVo.setCreateDate(model.getCreateTime());

            // 查询回答的所有二级评论
            List<Comment> twoComments = commentMapper.getCommentsByMidAndPid(mid, model.getId());
            List<Long> uidList2 = twoComments.stream().map(Comment::getUid).toList();
            List<User> userList2 = new ArrayList<>();
            Map<Long, User> userMap2 = new HashMap<>();
            if (!uidList2.isEmpty()) {
                userList2 = userMapper.listByIds(uidList2);
                userMap2 = userList2.stream().collect(Collectors.toMap(User::getUserId, user2 -> user2));
            }

            List<CommentVo> commentVo2List = new ArrayList<>();

            for (Comment twoComment : twoComments) {
                CommentVo commentVo2 = ConvertUtils.sourceToTarget(twoComment, CommentVo.class);
                commentVo2.setCreateDate(twoComment.getCreateTime());
                User user2 = userMap2.get(twoComment.getUid());

                if (user2 != null){
                    commentVo2.setUsername(user2.getUsername());
                    commentVo2.setAvatar(user2.getAvatar());
                }

                if (commentVo2.getReplyUid() != 0) {
                    User replyUser = userMapper.getUserById(commentVo2.getReplyUid());
                    commentVo2.setReplyName(replyUser.getUsername());
                }

                String agreeCommentKey2 = PlatformConstant.AGREE_COMMENT_KEY + twoComment.getId();
                commentVo2.setIsAgree(redisUtils.sIsMember(agreeCommentKey2, String.valueOf(uid)));

                commentVo2List.add(commentVo2);
            }

            String agreeCommentKey = PlatformConstant.AGREE_COMMENT_KEY + model.getId();
            commentVo.setIsAgree(redisUtils.sIsMember(agreeCommentKey, String.valueOf(uid)));
            commentVo.setChildrenComments(commentVo2List);
            commentVoList.add(commentVo);
        }

        // 封装分页返回结果
        IPage<CommentVo> resPage = new IPage<>();
        resPage.setRecords(commentVoList);
        resPage.setTotal(pageInfo.getTotal());
        resPage.setPageNum(pageInfo.getPageNum());
        resPage.setPageSize(pageInfo.getPageSize());

        return resPage;
    }

    @Override
    public IPage<CommentVo> getAllOneCommentByPostId(long page, long limit, Long mid, Long uid) {
        IPage<CommentVo> resPage = new IPage<>();
        //得到当前图片下的所有一级评论
        List<Comment> commentList = commentMapper.getCommentsByMidAndPid(mid,0L);
        PageInfo<Comment> commentOnePage = new PageInfo<>(commentList);

        if (commentOnePage.getTotal() == 0) {
            return resPage;
        }
        return getCommentVoIPage(uid, resPage, commentOnePage);
    }

    @Override
    public IPage<CommentVo> getAllTwoCommentByOneId(long page, long limit, Long id, Long uid) {
        List<Comment> commentList = commentMapper.getCommentsByPid(id);
        IPage<CommentVo> resPage = new IPage<>();
        PageInfo<Comment> commentTwoPage = new PageInfo<>(commentList);
        return getCommentVoIPage(uid, resPage, commentTwoPage);
    }

    @Override
    public List<CommentVo> getAllTwoComment(Long id, Long uid) {
        List<Comment> commentTwoList = commentMapper.getCommentsByPid(id);
        List<CommentVo> commentVoList = new ArrayList<>();

        CommentVo commentVo;
        for (Comment model : commentTwoList) {
            commentVo = ConvertUtils.sourceToTarget(model, CommentVo.class);
            commentVo.setCreateDate(model.getCreateTime());
            //判断当前评论是否点赞
            String agreeCommentKey = PlatformConstant.AGREE_COMMENT_KEY + model.getId();

            commentVo.setIsAgree(redisUtils.sIsMember(agreeCommentKey, uid));

            commentVoList.add(commentVo);
        }
        return commentVoList;
    }

    @Override
    public List<CommentVo> getAllReplyComment(long page, long limit, Long uid) {
        HashMap<Long, CommentVo> map = new HashMap<>();
        List<CommentVo> replyCommentList = new ArrayList<>();
        //得到当前用户发布的所有作品
        List<Post> postList = postMapper.getListByUid(uid, null);

        for (Post model : postList) {
            //得到当前笔记的所有一级评论
            List<Comment> commentList = commentMapper.getCommentsByMidAndPid(model.getId(), 0L);

            if (!commentList.isEmpty()) {
                List<Long> oneUids = commentList.stream().map(Comment::getUid).toList();
                List<User> oneUserList = userMapper.listByIds(oneUids);
                Map<Long, User> oneUserMap = new HashMap<>();

                oneUserList.forEach(item -> {
                    oneUserMap.put(item.getUserId(), item);
                });

                for (Comment e : commentList) {
                    if (Objects.equals(e.getUid(), uid)) {
                        continue;
                    }
                    User user = oneUserMap.get(e.getUid());
                    CommentVo commentVo = ConvertUtils.sourceToTarget(e, CommentVo.class);
                    commentVo.setCover(model.getCover())
                            .setUsername(user.getUsername())
                            .setAvatar(user.getAvatar())
                            .setCreateDate(e.getCreateTime());
                    map.put(commentVo.getId(), commentVo);
                }
            }

            //得到当前笔记的所有二级评论
            List<Comment> twoCommentList = commentMapper.getTwoCommentsByMidAndPid(model.getId(), 0L);

            if (!twoCommentList.isEmpty()) {
                List<Long> twoUids = twoCommentList.stream().map(Comment::getUid).collect(Collectors.toList());

                List<User> twoUserList = userMapper.listByIds(twoUids);
                Map<Long, User> twoUserMap = new HashMap<>();

                twoUserList.forEach(item -> {
                    twoUserMap.put(item.getId(), item);
                });

                for (Comment comment : twoCommentList) {

                    //如果二级评论是当前笔记的作者评论
                    if (Objects.equals(comment.getUid(), uid)) {
                        continue;
                    }

                    User user = twoUserMap.get(comment.getUid());

                    //得到原评论
                    Comment sourceComment = commentMapper.getById(comment.getReplyId());
                    User sourceUser = userMapper.getUserById(sourceComment.getUid());

                    CommentVo commentVo = ConvertUtils.sourceToTarget(comment, CommentVo.class);
                    commentVo.setCover(model.getCover())
                            .setUsername(user.getUsername())
                            .setAvatar(user.getAvatar())
                            .setContent(comment.getContent())
                            .setReplyUid(sourceComment.getUid())
                            .setReplyName(sourceUser.getUsername())
                            .setCreateDate(comment.getCreateTime())
                            .setReplyContent(sourceComment.getContent());

                    map.put(commentVo.getId(), commentVo);
                }
            }
        }

        //得到当前用户的所有评论(有一级评论和二级评论)
        List<Comment> commentList = commentMapper.getCommentsByUid(uid);

        for (Comment model : commentList) {
            //得到所有的回复评论
            List<Comment> commentList2 = commentMapper.getCommentsByReplyId(model.getId());

            if (!commentList2.isEmpty()) {
                List<Long> uids = commentList2.stream().map(Comment::getUid).toList();

                List<User> userList = userMapper.listByIds(uids);
                Map<Long, User> userMap = new HashMap<>();
                userList.forEach(item -> {
                    userMap.put(item.getId(), item);
                });

                List<Long> mids = commentList2.stream().map(Comment::getMid).collect(Collectors.toList());

                List<Post> imgList = postMapper.listByIds(mids, null);

                Map<Long, Post> postMap = new HashMap<>();

                imgList.forEach(item -> {
                    postMap.put(item.getId(), item);
                });

                for (Comment e : commentList2) {

                    if (Objects.equals(e.getUid(), uid) || map.containsKey(e.getId())) {
                        continue;
                    }

                    CommentVo commentVo = ConvertUtils.sourceToTarget(e, CommentVo.class);
                    User user = userMap.get(e.getUid());
                    Post post = postMap.get(e.getMid());

                    commentVo.setCover(post.getCover())
                            .setUsername(user.getUsername())
                            .setAvatar(user.getAvatar())
                            .setReplyContent(model.getContent())
                            .setCreateDate(e.getCreateTime())
                            .setContent(e.getContent());
                    map.put(commentVo.getId(), commentVo);
                }
            }
        }

        for (Long k : map.keySet()) {
            replyCommentList.add(map.get(k));
        }

        replyCommentList.sort((o1, o2) -> o2.getCreateDate().compareTo(o1.getCreateDate()));
        return PageUtils.getPages((int) page, (int) limit, replyCommentList).getList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delComment(Long id) {
        String commentStatKey = PlatformConstant.COMMENT_STATE + id;
        redisUtils.delete(commentStatKey);

        Comment comment = commentMapper.getById(Long.valueOf(id));

        if (comment.getPid() == 0) {
            //要把下面的所有二级评论给删了
            List<Comment> twoCommentList = commentMapper.getCommentsByPid(comment.getId());
            List<Long> cids = twoCommentList.stream().map(Comment::getId).collect(Collectors.toList());
            List<String> agreeCommentKeys = twoCommentList.stream().map(e -> PlatformConstant.AGREE_COMMENT_KEY + e.getId()).collect(Collectors.toList());
            List<String> commentStatKeys = twoCommentList.stream().map(e -> PlatformConstant.COMMENT_STATE + e.getId()).collect(Collectors.toList());

            redisUtils.delete(agreeCommentKeys);
            redisUtils.delete(commentStatKeys);
            commentMapper.removeBatch(cids);
        } else {
            Comment oneComment = commentMapper.getById(comment.getPid());
            oneComment.setTwoNums(oneComment.getTwoNums() - 1);
            platformProducer.sendMessage(RabbitMqConstant.COMMENT_STATE_EXCHANGE, RabbitMqConstant.COMMENT_STATE_KEY, oneComment);
        }
        commentMapper.removeById(Long.valueOf(id));
    }

    @Override
    public Map<String, Object> scrollComment(Long id, Long mid, Long uid) {
        Map<String, Object> resMap = new HashMap<>();

        Comment comment = commentMapper.getById(id);
        Long pid = comment.getPid();

        int page1 = 1;
        int page2 = 1;
        int limit1 = 6;
        int limit2 = 4;

        long total = 0;

        boolean flag = false;

        List<CommentVo> comments = new ArrayList<>();

        if (pid == 0) {
            while (!flag) {
                IPage<CommentVo> allOneCommentPage = this.getAllComment(page1, limit1, mid, uid);
                List<CommentVo> commentVoList = allOneCommentPage.getRecords();
                List<Long> pids = commentVoList.stream().map(CommentVo::getId).toList();
                if (pids.contains(id)) {
                    flag = true;
                    total = allOneCommentPage.getTotal();
                } else {
                    page1++;
                }
                comments.addAll(commentVoList);
            }
        } else {
            boolean flag2 = false;

            while (!flag) {
                IPage<CommentVo> allOneCommentPage = this.getAllComment(page1, limit1, mid, uid);
                List<CommentVo> commentVoList = allOneCommentPage.getRecords();
                List<Long> pids = commentVoList.stream().map(CommentVo::getId).toList();
                if (pids.contains(pid)) {
                    for (CommentVo commentVo : commentVoList) {
                        if (Objects.equals(commentVo.getId(), pid)) {
                            List<CommentVo> comments2 = new ArrayList<>();
                            flag = true;
                            total = allOneCommentPage.getTotal();
                            while (!flag2) {
                                IPage<CommentVo> allTwoCommentPage = this.getAllTwoCommentByOneId(page2, limit2, pid, uid);
                                List<CommentVo> commentVoList2 = allTwoCommentPage.getRecords();
                                List<Long> ids = commentVoList2.stream().map(CommentVo::getId).toList();
                                if (ids.contains(id)) {
                                    flag2 = true;
                                } else {
                                    page2++;
                                }
                                comments2.addAll(commentVoList2);
                            }
                            commentVo.setChildrenComments(comments2);
                        }
                    }
                } else {
                    page1++;
                }
                comments.addAll(commentVoList);
            }
        }

        resMap.put("records", comments);
        resMap.put("total", total);
        resMap.put("page1", page1);
        resMap.put("page2", page2);

        return resMap;
    }

    @Override
    public Comment getById(Long id) {
        return commentMapper.getById(id);
    }

    private IPage<CommentVo> getCommentVoIPage(Long uid, IPage<CommentVo> resPage, PageInfo<Comment> commentOnePage) {
        List<CommentVo> commentVoList = new ArrayList<>();
        CommentVo commentVo;

        List<Comment> commentList = commentOnePage.getList();

        List<Long> uidList = commentList.stream().map(Comment::getUid).collect(Collectors.toList());

        List<User> userList = userMapper.listByIds(uidList);

        Map<Long, User> userMap = new HashMap<>();

        userList.forEach(item -> userMap.put(item.getUserId(), item));

        for (Comment model : commentList) {
            commentVo = ConvertUtils.sourceToTarget(model, CommentVo.class);
            User user = userMap.get(model.getUid());
            commentVo.setUsername(user.getUsername());
            commentVo.setAvatar(user.getAvatar());
            commentVo.setCreateDate(model.getCreateTime());

            if (commentVo.getReplyUid() != 0) {
                User replyUser = userMapper.getUserByUserId(commentVo.getReplyUid());
                commentVo.setReplyName(replyUser.getUsername());
            }

            String agreeCommentKey = PlatformConstant.AGREE_COMMENT_KEY + model.getId();

            commentVo.setIsAgree(redisUtils.sIsMember(agreeCommentKey, String.valueOf(uid)));

            //判断当前评论是否点赞
            commentVoList.add(commentVo);
        }
        resPage.setRecords(commentVoList);
        resPage.setTotal(commentOnePage.getTotal());
        resPage.setPageNum(commentOnePage.getPageNum());
        resPage.setPageSize(commentOnePage.getPageSize());
        return resPage;
    }
}
